package cn.lili.common.utils;


import javax.crypto.Cipher;
import java.security.*;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;

public class RSAPCIUtil {

    public static void main(String[] args) throws Exception {
        //加密
        PublicKey publicKey = RSAPCIUtil.getPublicKey("30820122300d06092a864886f70d01010105000382010f003082010a0282010100ed8de5c64d2592e572bbbab4636a1bdb8352fdda8aa6621787f34d6decf4d9c502fe0b9db592af82b5f7394e35e3fd203063bb569f54695705166fe401329cf4bcf653c409315d6711a50d85d22d34571834fbc31c58c51ab33d4571f2c52a2856da26d0949e2f43f7318948e2d7f2ea8863c7b6478c80adf9c27802dafdee75c693a2d7b13f4462a1807afeeda1e4ac79f04730f9ecccd1347490caab0ef70fa8b8a541d9ac9bdab3c5a2d167fa6f3fdd835a956e347dc3d442f4cbf1d88744fd3d664c3490b598b580abcfacca7fcd5ed41b21b65f523eb8121e806dddfce60a5d49d213822e6b8ed1ec6c78cbd0f781cd3b94c23d541349f37bf7040501370203010001");
        byte[] bytes = RSAPCIUtil.encrypt("110669eb2a9-f4fd-4d87-b49f-55793759f6091FY_TEST_01".getBytes(),publicKey);
        System.out.println(RSAPCIUtil.byte2Hex(bytes));

        //解密
        byte[] sign = RSAPCIUtil.hex2Byte(RSAPCIUtil.byte2Hex(bytes));
        PrivateKey privateKey = RSAPCIUtil.getPrivateKey("308204be020100300d06092a864886f70d0101010500048204a8308204a40201000282010100ed8de5c64d2592e572bbbab4636a1bdb8352fdda8aa6621787f34d6decf4d9c502fe0b9db592af82b5f7394e35e3fd203063bb569f54695705166fe401329cf4bcf653c409315d6711a50d85d22d34571834fbc31c58c51ab33d4571f2c52a2856da26d0949e2f43f7318948e2d7f2ea8863c7b6478c80adf9c27802dafdee75c693a2d7b13f4462a1807afeeda1e4ac79f04730f9ecccd1347490caab0ef70fa8b8a541d9ac9bdab3c5a2d167fa6f3fdd835a956e347dc3d442f4cbf1d88744fd3d664c3490b598b580abcfacca7fcd5ed41b21b65f523eb8121e806dddfce60a5d49d213822e6b8ed1ec6c78cbd0f781cd3b94c23d541349f37bf7040501370203010001028201007504e6d0e1f4e5e1503ce8d26e7c791f8f97e4c3f073d8b345bd950156ee208e3b94a06d3e61f5423ad7ff6b2701d991af3499b6a88300e72472f2884b0cbf239227f788848cc4397f0822daae77a526581ed57c3decfe9f51f7174bf3eeaaff37157c7458197a280d4f6214bce5eadc1c76a68d04bed22ea16f6a05e2119f9282c7de5e2c3f8b91567d97bbbf03bd4f3d3bfde714874e3e1f02ab12ec9f1c86d2b82807f38ec118c9d568c999c28b07333d94dfa3960296225652367c5967f504732e237cdf5f4a9f099a2f0a350b26837fe7afd5a60cdd6f6b96898d97b463eb01a4d7ec82ca50b4c984f55c9d2b2ee308117d3579d723f95c4833861be77102818100fce58bc8f8377d3c8ae7bbc9eed5c057480d41d9b04afa1a162461005e46032e3b908a90526a40ea5b55a122f568eab9e332f09442b784e5285a61209af1c89cc7d9d0b49ed8e5a21090c5456cbbab7b28c49bb4c2b846519b247c375ac8a38aefeadd66d964a752427360fbdf5a033410d7ed617111d5c3d831bb57be9a9bb102818100f078279b47d4ddcf1831ef9a818908ab8796f527e1533bb2a3db1661f162ec6331dbb0f515cb769afb76b0bd0bbaf8dbd6c65419d1df775717e0576595bed5fba76dd94468dd5096857039d43a4ad6c04b719961b87842cee0ea6e3ecffad608c91143f5f37fce725e4eff189d4331bec6df98fddbe47b495ec35887a7666d6702818100aa534389303bd71a2e87927f975ed9332fb0d617f2571d08f2bebfa20adc6689fdce04b4dcfcf9099e4816c5bfb950d5af82806baa5f859efd4599c2e6f84edfe0edff5596e2880e55cfdb3d3dd54bd2200c02be84a2ada18ab6d0fe4b23749a3fa9d441cfcaadb28654c50dfe48d67d7b96a2270d8b3088bd94091438a852910281800e540a801d2298e8db1736a0ad313e1c0263434e5205c49bdb599596d66c3f8b958ff29657eb1cb11564b8193b96b3775defc95914814d3a1a491acc09c52a38e97c092323607489d37fa1627ceab2ab1893b740f45dee15279fb05bfbc68574568eb9cef0e85fa3beecd41462627b975380453c731ac521d50cacbcc083c0e70281810081208d44361416bfdfb433ea1cadf13914783415adef22f37a0ef18dcc61b8195cfb4c1642f17829a71ed24a27effd848ce95973735681e52225ec1dfcd9b0178a23c0433feb9ed83466cb6fa63e0a77f04e4b5ad0ed76b6586999c9cea29f87486d6a60cc965a89f189e77ea0632cda06123eeb14024aa1f43f40b54dee7b8e");
        byte[] resultbytes = RSAPCIUtil.decrypt(sign, privateKey);
        System.out.println(new String(resultbytes));
    }

    private static final String ALGORITHM = "RSA";

    /**
     * 公钥加密
     *
     * @param data      待加密数据
     * @param publicKey 公钥
     * @return
     */
    public static byte[] encrypt(byte[] data, PublicKey publicKey) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.ENCRYPT_MODE, publicKey);
        return cipher.doFinal(data);
    }

    /**
     * 私钥解密
     *
     * @param data       待解密数据
     * @param privateKey 私钥
     * @return
     */
    public static byte[] decrypt(byte[] data, PrivateKey privateKey) throws Exception {
        Cipher cipher = Cipher.getInstance(ALGORITHM);
        cipher.init(Cipher.DECRYPT_MODE, privateKey);
        return cipher.doFinal(data);
    }

    /**
     * 生成密钥对
     *
     * @return
     */
    public static KeyPair generateKeyPair() throws Exception {
        KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(ALGORITHM);
        keyPairGenerator.initialize(2048);
        return keyPairGenerator.generateKeyPair();
    }

    /**
     * 获取RSA私钥
     *
     * @param privateKeyString
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    public static PrivateKey getPrivateKey(String privateKeyString) throws InvalidKeySpecException, NoSuchAlgorithmException {
        // 获取私钥
        return KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(hex2Byte(privateKeyString)));
    }

    /**
     * 获取RAS公钥
     *
     * @param publicKeyString 加密后字符串形式公钥
     * @return
     * @throws InvalidKeySpecException
     * @throws NoSuchAlgorithmException
     */
    public static PublicKey getPublicKey(String publicKeyString) throws InvalidKeySpecException, NoSuchAlgorithmException {
        return KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(hex2Byte(publicKeyString)));
    }

    /**
     * 将byte[]数组转换成16进制字符。一个byte生成两个字符，长度对应1:2
     *
     * @param bytes，输入byte[]数组
     * @return 16进制字符
     */
    public static String byte2Hex(byte[] bytes) {
        if (bytes == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder();
        // 遍历byte[]数组，将每个byte数字转换成16进制字符，再拼接起来成字符串
        for (byte aByte : bytes) {
            // 每个byte转换成16进制字符时，bytes[i] & 0xff如果高位是0，输出将会去掉，所以+0x100(在更高位加1)，再截取后两位字符
            builder.append(Integer.toString((aByte & 0xff) + 0x100, 16).substring(1));
        }
        return builder.toString();
    }

    /**
     * 将16进制字符转换成byte[]数组。与byte2Hex功能相反。
     *
     * @param string 16进制字符串
     * @return byte[]数组
     */
    public static byte[] hex2Byte(String string) {
        if (string == null || string.isEmpty()) {
            return null;
        }
        // 因为一个byte生成两个字符，长度对应1:2，所以byte[]数组长度是字符串长度一半
        byte[] bytes = new byte[string.length() / 2];
        // 遍历byte[]数组，遍历次数是字符串长度一半
        for (int i = 0; i < string.length() / 2; i++) {
            // 截取没两个字符的前一个，将其转为int数值
            int high = Integer.parseInt(string.substring(i * 2, i * 2 + 1), 16);
            // 截取没两个字符的后一个，将其转为int数值
            int low = Integer.parseInt(string.substring(i * 2 + 1, i * 2 + 2), 16);
            // 高位字符对应的int值*16+低位的int值，强转成byte数值即可
            // 如dd，高位13*16+低位13=221(强转成byte二进制11011101，对应十进制-35)
            bytes[i] = (byte) (high * 16 + low);
        }
        return bytes;
    }
}
